﻿package de.gameduell.framework.game.watch {
	import de.gameduell.framework.debug.Debug;
	
	 /**
	 * Flexible, multi-functional stopwatch. Used in every GD game to keep
	 * track of either global or individual time limits. Most singleplayer
	 * games use the singleton variant class -> SingleStopWatch
	 */
	import de.gameduell.framework.security.SecureNumber;
	import de.gameduell.framework.util.Tools;
	
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
//	import flash.utils.getTimer;	

	//	import flash.utils.getTimer;	

	public class StopWatch extends EventDispatcher {

		public static var TIMER_TICK:String       = "onTimerTick";
		public static var START_TIME:String       = "onStartTime";
		public static var STOP_TIME:String        = "onStopTime";	
		public static var PAUSE_TIME:String       = "onPauseTime";
		public static var UNPAUSE_TIME:String     = "onUnPauseTime";
		public static var TIME_OVER:String        = "onTimeOver";
		public static var CHANGE_IDENTITY:String  = "onChangeIdentitiy";
		public static var CHANGE_VISIBILTY:String = "onChangeVisibility";
		
		private const NULL:Number = 0;
		
		private var _startTime:Number;
		private var _visibility:Boolean; 
		private var _isRunning:Boolean;
		private var _isPaused:Boolean;
		private var _pauseTime:Number;
		private var _updateInterval:Number;
		private var _identity:String;	
		private var _timeOver:Boolean;
		private var _totalTime:Number;
		private var tickTimer:Timer;
		private var _endTime:SecureNumber;
		private var lastUpdate:Number;
		private var singleTime:SingleTime;

		/**
		 * @param endTime	the time the StopWatch will run
		 */
		public function StopWatch(endTime:Number = Infinity) {
			_endTime = new SecureNumber(endTime);
			_updateInterval = 500;
			_visibility = true;
			_isRunning = false;
			_isPaused = false;
			_timeOver = false;
			_totalTime = 0;
			_pauseTime = 0;
			_startTime = 0;
			singleTime = SingleTime.getInstance();
			tickTimer = new Timer(_updateInterval, 0);
			tickTimer.addEventListener(TimerEvent.TIMER, updateOnEvent);
		} 

		/*###################################################
		#		   			   SETTER						#
		#####################################################*/
		
		public function set startTime(aStartTime:Number):void {
			_startTime = aStartTime;			
		}
		
		public function set pauseTime(aPauseTime:Number):void {
			_pauseTime = aPauseTime;			
		}
		
		/**
		 * sets the visibility
		 * the visibility is just a flag the real visibility of 
		 * the watch has to be implemented in the view
		 */
		public function set visibility(isVisible:Boolean):void {
			_visibility = isVisible;
			dispatchEvent(new Event(CHANGE_VISIBILTY));
		}

		/**
		 * sets the endTime
		 * the endTime is the time the Stopwatch run
		 */
		public function set endTime(newEndTime:Number):void {
			_endTime = new SecureNumber(newEndTime);
		}

		/**
		 * sets the identiy
		 * the identity identifies the owner of the watch
		 * this is important when you have different players
		 * but just one watch
		 * 
		 * calls the _onIdentityChanged callBack with the new identity
		 */
		public function set identity(newIdentity:String):void {
			_identity = newIdentity;
			dispatchEvent(new DataEvent(CHANGE_IDENTITY, false,false,newIdentity));
		}

		public function set updateInterval(interval:Number):void {
			_updateInterval = interval;
		}

		/*###################################################
		#		   			   GETTER						#
		#####################################################*/

		public function get pauseTime():Number {
			return _pauseTime;
		}		

		/**
		 * @return _totalTime = the total time the stopwatch runs
		 */
		public function get totalTime():Number {
			return _totalTime;
		}

		/**
		 * @return _identity	the identiy of the watch
		 */
		public function get identity():String {
			return _identity;
		}

		/**
		 * @return	the passed time in milliseconds
		 */
		public function get passedTime():Number {
			if(_startTime == 0){
				return 0;
			}
			return _isPaused?_pauseTime:(singleTime.getPassedTime() - _startTime); //dkum
		}
		
		/**
		 * @return	the passed minutes
		 */
		public function get passedMinutes():Number{
			if(_startTime == 0){
				return 0;
			}
			return Math.floor(passedTime / 60000);
		}
		
		/**
		 * returns the passed seconds
		 * example: when 3:21 minutes are passed
		 * it will return 21 seconds
		 * 	
		 * @return	the passed seconds
		 */
		public function get passedSeconds():Number {
			if(_startTime == 0){
				return 0;
			}
			return Math.floor((passedTime % 60000) / 1000);
		}

		/**
		 * @return	the remaining time in milliseconds
		 */
		public function get remainingTime():Number {
			if(_startTime == 0){
				return 0;
			}
			return (_endTime.getValue() - passedTime) < 0 ? 0 : (_endTime.getValue() - passedTime);
		}

		/**
		 * @return	the remaining minutes
		 */
		public function get remainingMinutes():Number {
			if(_startTime == 0){
				return 0;
			}
			return Math.floor((remainingTime % 3600000) / 60000);
		}
		
		/**
		 * @return	the remaining minutes
		 */
		public function get remainingHours():Number {
			if(_startTime == 0){
				return 0;
			}
			return Math.floor((remainingTime % 864000000) / 3600000);
		}
		
		
		public function get remainingFullTimeDigital():String{
			if(_startTime == 0){
				return "00:00:00";
			}
			return Tools.zeroize(remainingHours, 2)+":"+Tools.zeroize(remainingMinutes, 2)+":"+Tools.zeroize(remainingSeconds, 2);
		}
		
		/**
		 * return the passed time in the typical digital format as String.
		 * example: 01:30
		 *
		 * @return	the passed time in the typical digital format as String
		 */
		public function get passedTimeDigital():String{
			if(_startTime == 0){
				return "00:00";
			}
			return Tools.zeroize(passedMinutes, 2)+":"+Tools.zeroize(passedSeconds, 2);
		}
		
		
		/**
		 * return the passed time in the typical digital format as String.
		 * example: 01:30
		 *
		 * @return	the passed time in the typical digital format as String
		 */
		public function get remainingTimeDigital():String{
			if(_startTime == 0){
				return "00:00";
			}
			return Tools.zeroize(remainingMinutes, 2)+":"+Tools.zeroize(remainingSeconds, 2);
		}
			

		/**
		 * returns the remaining seconds
		 * example: when 3:21 minutes are remaining
		 * 			it will return 21 seconds
		 * 
		 * @return	the remaining seconds
		 */
		public function get remainingSeconds():Number {
			return Math.floor((remainingTime % 60000) / 1000);
		}

		/**
		 * @return	the visibility
		 */
		public function get visibility():Boolean {
			return _visibility;
		}

		/**
		 * @return	the passed time in percent
		 */
		public function get passedTimePercent():Number {
			if(_startTime == 0){
				return 0;
			}
			var onePercent:Number  = (_endTime.getValue()) / 100;
			var currPercent:Number = (singleTime.getPassedTime() - _startTime) / onePercent;
			return currPercent;
		}

		/**
		 * nice when you have a round watch
		 * 	
		 * @return	the passed time in degree 
		 */
		public function get passedTimeDegree():Number {
			if(_startTime == 0){
				return 0;
			}
			var oneDegree:Number  = (_endTime.getValue()) / 360;
			var currDegree:Number = (singleTime.getPassedTime() - _startTime) / oneDegree;
			return currDegree;
		}

		public function get endTime():Number {
			return _endTime.getValue();
		}

		/**
		 * @return	true when the watch runs
		 */
		public function get isRunning():Boolean {
			return _isRunning;
		}

		/**
		 * @return	true when the watch is paused
		 */
		public function get isPaused():Boolean {
			return _isPaused;
		}

		/**
		 * @return	true when the time is over
		 */
		public function get timeOver():Boolean {
			return _timeOver;
		}
		
		public function get startTime():Number {
			return _startTime;
		}

		/*###################################################
		#		   			   	MISC						#
		#####################################################*/
		
		/**
		 * starts the time
		 */
//		public function start():void {
//			dispatchEvent(new Event(START_TIME));
//			if(!_isRunning && !_isPaused) {
//				_isRunning = true;
//				tickTimer = new Timer(_updateInterval, 0);
//				tickTimer.addEventListener(TimerEvent.TIMER, updateOnEvent);
//				tickTimer.start();
//				_startTime = getTimer();
//				update();
//			}
//		}
		public function start():void {
			dispatchEvent(new Event(START_TIME));
			if(!_isRunning && !_isPaused) {
				_isRunning = true;
				_timeOver  = false;
				lastUpdate = NULL;
				tickTimer.start();
				_startTime = singleTime.getPassedTime();
				update();
			}
		}

		/**
		 * toggle the pause
		 */
		public function togglePause():void {
			if(_isPaused) {
				unPause();
			}else {
				pause();
			}
		}

		public function addTime(addedTime:Number):void {
//			singleTime.getPassedTime() - _startTime)
			var result:Number = _startTime + addedTime;
			if(singleTime.getPassedTime() - result <0){
				_startTime = singleTime.getPassedTime();
			}else{
				_startTime += addedTime;
			}
		}

		/**
		 * unpause the time
		 */
		public function unPause(event:Event = null):void {
			if(_isPaused) {
				dispatchEvent(new Event(UNPAUSE_TIME));
				_isRunning = true;
				_isPaused = false;
				_startTime = singleTime.getPassedTime() - _pauseTime;
				tickTimer.start();
				update();
			}
		}
		
		/**
		 * pause the time
		 */
		public function pause(event:Event = null):void {
			if(!_timeOver && !_isPaused && _isRunning) {
				dispatchEvent(new Event(PAUSE_TIME));
				lastUpdate = NULL;
				_isRunning = false;
				_isPaused = true;
				tickTimer.stop();
				_pauseTime = singleTime.getPassedTime() - _startTime;
				update(); //dkum
			}
		}

		/**
		 * stops the time
		 */
		public function stop():void {
			lastUpdate = NULL;
			dispatchEvent(new Event(STOP_TIME));
			_isRunning = false;
			_isPaused  = false;
			_startTime = singleTime.getPassedTime();
			tickTimer.stop();
		}

		/**
		 * restarts the time
		 */
		public function restart():void {
			stop();
			start();
		}

		private function updateOnEvent(event:TimerEvent):void {
			update();
		}

		private function update():void {
			var currTime:Number = passedTime;
		
			var currUpdate:Number = singleTime.getPassedTime();
			if(lastUpdate != NULL) {
				_totalTime += currUpdate - lastUpdate;
			}	
			lastUpdate = currUpdate;
		
			if(currTime > _endTime.getValue()) {
				_timeOver = true;
				_isRunning = false;
				tickTimer.stop();

				dispatchEvent(new Event(TIME_OVER));
			}
			dispatchEvent(new Event(TIMER_TICK));			
		}	
	}
}